package com.huawei.aiservice.impl.strategy;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.huawei.aiservice.entity.pojo.Defect;
import com.huawei.aiservice.entity.pojo.FileEntity;
import com.huawei.aiservice.entity.pojo.FileRule;
import com.huawei.aiservice.entity.pojo.Rule;
import com.huawei.aiservice.enums.ReportStatus;
import com.huawei.aiservice.exception.BusinessException;
import com.huawei.aiservice.service.DefectService;
import com.huawei.aiservice.service.FileRuleService;
import com.huawei.aiservice.utils.AIUtils;
import com.huawei.aiservice.utils.FileUtils;
import com.vladsch.flexmark.ast.FencedCodeBlock;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.ast.Node;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;

@Slf4j
public class InConsistentRule extends RuleStrategy {
    /**
     * 执行该规则，并把返回的数据入库
     *
     * @param map
     * @param executor
     * @param rule
     * @param defectService
     */
    @Override
    public void execute(Map<MultipartFile, FileEntity> map, ThreadPoolTaskExecutor executor,
                        Rule rule, DefectService defectService, String fragment, FileRuleService fileRuleService){
        String systemPrompt = FileUtils.getFileContent("prompt/inconsistent.md");
        if (systemPrompt == null) {
            String errorMsg = "system prompt is null";
            log.error(errorMsg);
            throw new BusinessException(errorMsg);
        }

        for (MultipartFile file : map.keySet()) {
            try {
                solve(executor, map.get(file), rule, defectService, systemPrompt, fragment, fileRuleService);
            } catch (IOException e) {
                String errorMsg = "some error in $solve";
                log.error(errorMsg, e);
                throw new BusinessException(errorMsg, e);
            }
        }
    }

    // TODO 增量扫描


    /**
     * 对单个文件单个规则处理
     *
     * @param executor
     * @param fileEntity
     * @param rule
     * @param defectService
     * @param systemPrompt
     * @param fragment
     */
    private void solve(ThreadPoolTaskExecutor executor, FileEntity fileEntity, Rule rule,
                       DefectService defectService, String systemPrompt, String fragment, FileRuleService fileRuleService) throws IOException {
        Parser parser = Parser.builder().build();
        File inputFile = new File(fileEntity.getFilePath());
        String inputString = new String(Files.readAllBytes(Paths.get(inputFile.getAbsolutePath())));
        Document document = parser.parse(inputString);

        FileRule fileRule = new FileRule(fileEntity.getFileId(), rule.getRuleId(), ReportStatus.PROCESSING);
        fileRuleService.save(fileRule);

        StringBuilder history = new StringBuilder();

        int cnt = 0;
        for (Node node : document.getChildren()) {
            if (node instanceof FencedCodeBlock) continue;
            cnt++;
        }
        CountDownLatch countDownLatch = new CountDownLatch(cnt);
        // 遍历AST节点
        for (Node node : document.getChildren()) {
            if (node instanceof FencedCodeBlock) continue;
            String prompt = systemPrompt.replace("{{placeholder}}", history);
            InConsistentCallable task = new InConsistentCallable(fileRule.getFileRuleId(),
                    prompt, node.getChars().toString(), inputFile, fileRuleService, countDownLatch, defectService);
            executor.submit(task);
            String string = node.getChars().toString();
            history.append(string);
        }
    }

    static class InConsistentCallable implements Callable<List<Defect>> {
        private final int fileRuleId;

        private final String systemPrompt;

        private final String nodeString;

        private final File file;

        private final FileRuleService fileRuleService;

        private final CountDownLatch countDownLatch;

        private final DefectService defectService;

        public InConsistentCallable(int fileRuleId, String systemPrompt, String nodeString, File file,
                                    FileRuleService fileRuleService, CountDownLatch countDownLatch,
                                    DefectService defectService) {
            this.fileRuleId = fileRuleId;
            this.systemPrompt = systemPrompt;
            this.nodeString = nodeString;
            this.file = file;
            this.fileRuleService = fileRuleService;
            this.countDownLatch = countDownLatch;
            this.defectService = defectService;
        }

        @Override
        public List<Defect> call() throws Exception {
            String responseJson = null;
            while (responseJson == null &&
                    fileRuleService.getById(fileRuleId).getCheckStatus().equals(ReportStatus.PROCESSING)) {
                responseJson = AIUtils.getResponse(systemPrompt, nodeString);
            }
            List<Defect> defectList = convert(responseJson);
            List<Defect> result = new ArrayList<>();
            for (Defect defect : defectList) {
                int lineNum = getLineNum(file, defect.getProblematicSentence());
                if (lineNum == -1) {
                    continue;
                }
                defect.setProblematicLineNum(lineNum);
                defect.setFileRuleId(fileRuleId);
                result.add(defect);
            }

            // 保存
            defectService.saveBatch(result);
            countDownLatch.countDown();
            if (countDownLatch.getCount() == 0) {
                LambdaUpdateWrapper<FileRule> wrapper = Wrappers.<FileRule>lambdaUpdate().eq(FileRule::getFileRuleId, fileRuleId)
                        .set(FileRule::getCheckStatus, ReportStatus.SUCCESS);
                fileRuleService.update(wrapper);
            }

            return result;
        }

        private int getLineNum(File file, String target) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(Files.newInputStream(file.toPath())))) {
                String line;
                int lineNumber = 0;
                while ((line = reader.readLine()) != null) {
                    lineNumber++;
                    if (line.contains(target)) {
                        return lineNumber;
                    }
                }
            } catch (IOException e) {
                log.error("Error reading file", e);
                return -1;
            }
            return -1; // 如果未找到目标字符串，返回 -1
        }

        public static List<Defect> convert(String json) throws JsonProcessingException {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
            Defect[] defects = objectMapper.readValue(json, Defect[].class);
            return Arrays.asList(defects);
        }
    }

}
